﻿using Serilog;
using SuperSocket.SocketBase;
using SuperSocket.SocketEngine;
using System;
using System.Linq;
using System.Threading;
using WebSocketSharp;

namespace ParkService.Core
{
    public class WsClient
    {
        readonly string _serverWsUrl;
        readonly int _port;
        readonly int _hubId;
        readonly ILogger _logger;

        bool working = true;
        Thread _wsThread;
        Thread _serialPortServerThread;
        WebSocket _webSocket;
        AppServer<SocketSession> _appServer;

        public WsClient(string serverWsUrl, int port, int hubId)
        {
            _serverWsUrl = serverWsUrl;
            _port = port;
            _hubId = hubId;
            _logger = Serilog.Log.Logger;
        }

        public void Start()
        {
            _wsThread = new Thread(connectWs);
            _wsThread.Start();

            _serialPortServerThread = new Thread(listenSerialPortServer);
            _serialPortServerThread.Start();
        }

        public void Stop()
        {
            working = false;
            if (_webSocket != null && _webSocket.IsAlive)
            {
                _webSocket.Close();
            }
            if (_appServer != null && _appServer.State == ServerState.Running)
            {
                _appServer.Stop();
            }
        }

        private void connectWs()
        {
            _webSocket = new WebSocket(_serverWsUrl);

            _webSocket.OnMessage += Ws_OnMessage;
            _webSocket.OnOpen += Ws_OnOpen;
            _webSocket.OnClose += Ws_OnClose;

            while (working)
            {
                if (!_webSocket.IsAlive)
                {
                    _webSocket.Connect();
                }
                Thread.Sleep(1000);
            }

        }
        private void Ws_OnOpen(object sender, EventArgs e)
        {
            _logger.Information("WebSocket OnOpen");
            if (_webSocket.IsAlive)
            {
                _webSocket.Send($"auth {_hubId}");
            }
        }

        private void Ws_OnClose(object sender, CloseEventArgs e)
        {
            _logger.Warning("WebSocket Close");
        }

        private void Ws_OnMessage(object sender, MessageEventArgs e)
        {
            _logger.Debug("WebSocket OnMessage:" + e.Data);
            if (!string.IsNullOrEmpty(e.Data))
            {
                var commands = e.Data.Split(' ');
                if (commands.Length > 0)
                {
                    // 转发命令到串口服务器
                    if ("send".Equals(commands[0], StringComparison.OrdinalIgnoreCase) && commands.Length > 2)
                    {
                        var session = _appServer.GetAllSessions().Where(m => m.ip == commands[1]).FirstOrDefault();
                        if (session != null)
                        {
                            var data = StringToByteArray(commands[2]);
                            session.TrySend(data, 0, data.Length);
                        }
                    }
                }
            }

        }

        /// <summary>
        /// 16进制字符串转byte[]
        /// </summary>
        /// <param name="hex"></param>
        /// <returns></returns>
        public static byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }

        private void listenSerialPortServer()
        {
            _appServer = new AppServer<SocketSession>();
            _appServer.NewSessionConnected += _appServer_NewSessionConnected;
            _appServer.SessionClosed += _appServer_SessionClosed;
            _appServer.NewRequestReceived += _appServer_NewRequestReceived;

            //Setup the appServer
            if (!_appServer.Setup(_port)) //Setup with listening port
            {
                _logger.Error("SerialPortServer Failed to setup");
                return;
            }
            if (!_appServer.Start())
            {
                _logger.Error("SerialPortServer Failed to Start");
                return;
            }
            _logger.Information("Listen TCP on {@_port}", _port);
            while (working)
            {
                Thread.Sleep(1000);
                if(_appServer.State == ServerState.Stopping || _appServer.State == ServerState.NotStarted)
                {
                    try
                    {
                        if (!_appServer.Start())
                        {
                            _logger.Error("SerialPortServer Failed to Start");
                            return;
                        }
                        _logger.Information("Listen TCP on {@_port}", _port);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("Try Listen TCP on {@_port} FAIL {@message}", _port, ex.Message);
                    }
                }
            }
        }

        private void _appServer_NewRequestReceived(SocketSession session, SuperSocket.SocketBase.Protocol.StringRequestInfo requestInfo)
        {
            _logger.Debug("NewRequestReceived {@sessionid} {@ip} key={@key} body={@body}", session.SessionID, session.RemoteEndPoint.Address.ToString(), requestInfo.Key, requestInfo.Body);
            _webSocket.Send($"received {new Message(_hubId, session.RemoteEndPoint.Address.ToString(), requestInfo.Key, requestInfo.Body)}");
        }

        private void _appServer_SessionClosed(SocketSession session, CloseReason value)
        {
            _logger.Information("SessionClosed {@sessionid} {@ip}", session.SessionID, session.RemoteEndPoint.Address.ToString());
            _webSocket.Send($"closed {new Message(_hubId, session.RemoteEndPoint.Address.ToString())}");
        }

        private void _appServer_NewSessionConnected(SocketSession session)
        {
            _logger.Information("NewSessionConnected {@sessionid} {@ip}", session.SessionID, session.RemoteEndPoint.Address.ToString());
            _webSocket.Send($"connected {new Message(_hubId, session.RemoteEndPoint.Address.ToString())}");
        }
    }
}
